// create by zhifeng.ding
// using opencv for debug image algorithm

#ifndef ODEBUG_H
#define ODEBUG_H

#include <cstdint>
#include <map>
#include "opencv2/opencv.hpp"
#include <cassert>

using namespace cv;
class ODebug {
public:
    ODebug(int32_t width = 5000, int32_t height = 5000)
    {
        m_max_size = Size(0,0);
        m_img_map.clear();
        m_img_buf = NULL;
        
        
        if (width > 0 && height > 0) {
            m_max_size.width = width;
            m_max_size.height = height;
            m_img_buf = new uint8_t[width*height*3];
            assert(m_img_buf != NULL);
            m_img_map.clear();
        }
    }
    ~ODebug()
    {
        if (m_img_buf) {
            delete[] m_img_buf;
        }
        m_img_buf = NULL;
        m_img_map.clear();
        
    }
    
    // open image
    bool open(const std::string &path, int32_t flag, const std::string &name = "none")
    {
        m_image_cache = imread(path,flag);
        if (m_image_cache.empty()) {
            return false;
        }
        
        if (name != "none" && m_img_map.count(name) == 0) {
            m_img_map[name] = m_image_cache.clone();
        }
        
        return true;
    }
    
    // convert m_image_cache to buffer
    const uint8_t* to_buffer(bool isColor = false)
    {
        Mat temp;
        if (m_image_cache.empty()) {
            return NULL;
        }
        
        temp = m_image_cache;
        if (isColor) {
            if (m_image_cache.channels() == 1) {
                Mat color;
                cvtColor(m_image_cache,color,COLOR_GRAY2RGB);
                temp = color;
            }
        }
        else
        {
            if (m_image_cache.channels() == 3)
            {
                Mat gray;
                cvtColor(m_image_cache, gray, COLOR_RGB2GRAY);
                temp = gray;
            }
        }
        
        uint8_t *pDstBuf = m_img_buf;
        for (int32_t y = 0; y < temp.rows; y++) {
            uint8_t *pSrcBuf = temp.ptr<uint8_t>(y);
            
            memcpy(pDstBuf, pSrcBuf, temp.step1());
            
            pDstBuf += temp.step1();
        }
        
        return m_img_buf;
    }
    
    // m_image_cache push into map
    bool push_cache(const std::string &name)
    {
        if (m_image_cache.empty())
        {
            return false;
        }
        
        if (m_img_map.count(name) == 1)
        {
            return false;
        }
        
        m_img_map[name] = m_image_cache.clone();
        
        return true;
    }
    
    // convert outbuff into m_image_cache(RGB)
    Mat to_rgb_mat(Size &img_size, const uint8_t *pImgBuf, bool isColor = false)
    {
        Mat image;
        uint8_t *pSrcBuf = (uint8_t *)pImgBuf;
        if (isColor)
        {
            Mat temp(img_size, CV_8UC1);
            for (int32_t y = 0; y < img_size.height; y++) {
                uint8_t *pDst = temp.ptr<uint8_t>(y);
                memcpy(pDst, pSrcBuf, img_size.width);
                pSrcBuf += img_size.width;
            }
            image = temp;
        }else
        {
            Mat temp(img_size, CV_8UC3);
            for (int32_t y = 0; y < img_size.height; y++) {
                uint8_t *pDst = temp.ptr<uint8_t>(y);
                memcpy(pDst, pSrcBuf, img_size.width * 3);
                pSrcBuf += img_size.width * 3;
            }
            image = temp;
        }
        
        return image;
    }
    
    // add mat into map
    bool add(cv::Mat& mat, std::string& name)
    {
        if (m_img_map.count(name) == 1)
        {
            return false;
        }
        
        m_img_map[name] = mat.clone();
        
        return true;
    }
    
    // get mat by name
    Mat& get(const std::string &name)
    {
        return m_img_map[name];
    }
    
    Mat& get()
    {
        return m_image_cache;
    }
    
    void show(const std::string &winname = "default", const std::string &name = "cache")
    {
        namedWindow(winname,0);
        
        if (name == "cache")
        {
            imshow(winname,m_image_cache);
        }
        else
        {
            if (m_img_map.count(name) == 0)
            {
                std::cout << "no named : " + name << " image in map" << std::endl;
            }
            else
            {
                imshow(winname, m_img_map[name]);
            }
        }
        waitKey();
        destroyWindow(winname);
    }
    
    
    
    
    // save image
    void save(const std::string &name = "all")
    {
        if (name == "all") {

        } else if (name == "cache")
        {
            if (!m_image_cache.empty())
            {
                bool ret = imwrite("./" + name + ".jpg", m_image_cache);
            }
        }else
        {
            
        }
    }

    // draw some on canvas
    void draw_point(Mat &image, const Point &pt, int32_t size = 1, Scalar color = Scalar(0,0,255))
    {
        if (!image.empty())
        {
            if (size == 1)
            {
                image.at<Vec3b>(pt.x,pt.y)[0] = color.val[0];
                image.at<Vec3b>(pt.x,pt.y)[1] = color.val[1];
                image.at<Vec3b>(pt.x,pt.y)[2] = color.val[2];
            }
        }
    }
//
//    void draw_segm(Mat image, const Point &head, const Point &tail, Scalar color = Scalar(0,255,0));
//    
//    void draw_line(Mat image, float a, float b, float c, Scalar color = Scalar(255,0,0));
//
//    void draw_rect(Mat image, const Point &lt, const Point &br, Scalar color = Scalar(255,255,0));
    
private:
    cv::Mat m_image_cache;
    uint8_t* m_img_buf;
    cv::Size m_max_size;
    std::map<std::string, cv::Mat> m_img_map;
};



#endif // ODEBUG_H
